<?php
/**
 * EGP Framework
 *
 * LICENSE
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */

!defined('EGP_LIB_DIR') && die('Access Deny!');

/**
 * 数据库适配器抽像类
 *
 * @package    classes
 * @author     Akon(番茄红了) <aultoale@gmail.com>
 * @copyright  Copyright (c) 2008 (http://www.tblog.com.cn)
 * @license    http://www.gnu.org/licenses/gpl.html     GPL 3
 */
abstract class EGP_Db_Adapter_Abstract
{

    /**
     * 数据库连接配置信息
     *
     * @var array
     */
    protected $_options = array();

    /**
     * 数据库查询监视器
     *
     * @var EGP_Db_Monitor
     */
    protected $_monitor = null;

    /**
     * 数据库连接 (link)
     *
     * @var resource
     */
    protected $_connection = null;

    /**
     * 数据库关闭标识符
     *
     * @var boolean
     */
    protected $_closeFlag = false;

    /**
     * 构造方法
     *
     * @param  array  $options  数据库连接配置信息
     */
    public function __construct(array $options)
    {
    }

    /**
     * 获取数据库查询监视器
     *
     * @return EGP_Db_Monitor
     */
    public function getMonitor()
    {
        !$this->_monitor instanceof EGP_Db_Monitor && $this->_monitor = new EGP_Db_Monitor();
        return $this->_monitor;
    }

    /**
     * 获取数据库连接
     *
     * @return resource
     */
    public function getConnection()
    {
        $this->connect();
        return $this->_connection;
    }

    /**
     * 根据配置信息连接到 MySQL 数据库
     *
     * @throws EGP_Exception
     */
    public function connect()
    {
        if ($this->_closeFlag === true)
            throw new EGP_Exception('数据库连接已关闭');
        if (null == $this->_connection)
            $this->_connect();
    }

    /**
     * 根据配置信息连接到 MySQL 数据库
     */
    abstract protected function _connect();

    /**
     * 关闭数据库连接
     *
     */
    public function close()
    {
        $this->_closeFlag = true;
        $this->_connection = null;
        $this->_close();
    }

    /**
     * 打开指定的数据库
     *
     * @param  string  $dbname  数据库名称
     * @return boolean
     */
    abstract public function selectDb($dbname);

    /**
     * 关闭数据库连接
     */
    abstract protected function _close();

    /**
     * 返回上一个操作中的错误信息的数字编码
     *
     * @return integer
     */
    abstract public function errno();

    /**
     * 返回上一个操作产生的文本错误信息
     *
     * @return string
     */
    abstract public function error();

    /**
     * 执行一条查询语句
     *
     * @param  string  $sql
     * @return resource
     * @throws EGP_Exception
     */
    public function query($sql)
    {
        if ($sql instanceof EGP_Db_Select)
            $sql = $sql->__toString();
        $startTime = microtime(true);
        if (!$query = $this->_query($sql))
            throw new EGP_Exception($this->error());
        $this->getMonitor()->update($sql, microtime(true) - $startTime);
        return $query;
    }

    /**
     * 执行一条查询语句
     *
     * @param  string  $sql
     * @return resource
     */
    abstract protected function _query($sql);

    /**
     * 调用 sql 语句生成类
     *
     * @return EGP_Db_Select
     */
    public function select()
    {
        return new EGP_Db_Select($this);
    }

    /**
     * 插入数据到指定表中
     *
     * @param  string  $table   表名
     * @param  array   $data    需要插入的数据
     * @param  array   $schema  方案(数据库)名
     * @return integer          影响到的记录行数
     */
    public function insert($table, array $data, $schema = null)
    {
        if (is_array(current($data))) {
            $count = 0;
            foreach ($data as $val) {
                $count += $this->insert($table, $val, $schema);
            }
            return $count;
        } else {
            foreach ($data as $key => $val) {
                $cols[] = $this->quoteAs($key);
                $vals[] = $this->quote($val);
            }
            $columns = '(' . implode(', ', $cols) . ')';
            $values = '(' . implode(', ', array_values($vals)) . ')';

            !empty($schema) && $schema = $this->quoteIdent($schema) . '.';
            $sql = "INSERT INTO $schema" . $this->quoteAs($this->quotePrefix($table)) .
                   "\n\t$columns\nVALUES \n\t$values;\n";
            $this->query($sql);
            return $this->rowCount();
        }
    }

    /**
     * 更新指定表的数据
     *
     * @param  string  $table   表名
     * @param  array   $data    更新后的数据
     * @param  mixed   $where   更新条件
     * @param  array   $schema  方案(数据库)名
     * @return integer          影响到的记录行数
     */
    public function update($table, array $data, $where = '', $schema = null)
    {
        foreach ($data as $col => $val)
            $set[] = $this->quoteAs($col) . ' = ' . $this->quote($val);
        $where = $this->_whereExpr($where);
        !empty($schema) && $schema = $this->quoteIdent($schema) . '.';
        $sql = "UPDATE $schema" . $this->quoteAs($this->quotePrefix($table)) .
               ' SET ' . "\n\t" . implode(", \n\t", $set) .
               ($where ? "\nWHERE $where" : '');
        $this->query($sql);
        return $this->rowCount();
    }

    /**
     * 删除指定表中的数据
     *
     * @param  string  $table   表名
     * @param  mixed   $where   删除条件
     * @param  array   $schema  方案(数据库)名
     * @return integer          影响到的记录行数
     */
    public function delete($table, $where = '', $schema = null)
    {
        !empty($schema) && $schema = $this->quoteIdent($schema) . '.';
        $sql = "DELETE FROM $schema" . $this->quoteAs($this->quotePrefix($table)) .
               ($where ? " WHERE " . $this->_whereExpr($where) : '');
        $this->query($sql);
        return $this->rowCount();
    }

    /**
     * 添加 SQL 语句 LIMIT 限制
     *
     * @param  string   $sql
     * @param  integer  $count
     * @param  integer  $offset
     * @return string
     */
    abstract public function limit($sql, $count, $offset = 0);

    /**
     * 锁定表
     *
     * @return resource
     */
    abstract public function lock($table);

    /**
     * 解除表锁定
     *
     * @param  string  $table
     * @return resource
     */
    abstract public function unlock($table = null);

    /**
     * 开启事务处理
     *
     * @return resource
     */
    abstract public function beginTransaction();

    /**
     * 提交事务
     *
     * @return resource
     */
    abstract public function commit();

    /**
     * 回滚事务
     *
     * @return resource
     */
    abstract public function rollback();

    /**
     * 根据查询取出所有资料
     *
     * @param  string  $sql
     * @return array
     */
    abstract public function fetchAll($sql);

    /**
     * 根据查询取出所有数据，并作为关联数组及数字数组返回
     *
     * @param  string  $sql
     * @return array
     */
    abstract public function fetchArray($sql);

    /**
     * 根据查询取出所有数据，并作为数组对象返回
     *
     * @param  string  $sql
     * @return array
     */
    abstract public function fetchObj($sql);

    /**
     * 根据查询取出一行数据，并以为数组返回
     *
     * @param  string  $sql
     * @return array
     */
    abstract public function fetchRow($sql);

    /**
     * 根据查询取出第一行数据，并返回第一个字段的值
     *
     * @param  string  $sql
     * @return mixed
     */
    abstract public function fetchOne($sql);

    /**
     * 取得结果集中指定列的内容
     *
     * @param  string   $sql
     * @param  integer  $row
     * @return mixed
     */
    abstract public function result($sql, $row);

    /**
     * 取得上一次 MySQL 操作所影响的记录行数
     *
     * @return integer
     */
    abstract public function rowCount();

    /**
     * 取得结果集中行的数目
     *
     * @param  string  $sql
     * @return integer
     */
    abstract public function numRows($sql);

    /**
     * 取得上一次 INSERT 操作产生的主键 ID 值
     *
     * @return integer
     */
    abstract public function lastInsertId();

    /**
     * 添加引用及过滤转义符
     *
     * @param  mixed  $value
     * @return string
     */
    public function quote($value)
    {
        if ($value instanceof EGP_Db_Expr)
            return $value->__toString();
        if (is_array($value)) {
            foreach ($value as &$val)
                $val = $this->quote($val);
            return implode(', ', $value);
        }
        if (is_int($value) || is_float($value))
            return $value;
        return "'" . $this->_quote($value) . "'";
    }

    /**
     * 替换占位符为已添加引用及过滤转义符的值
     *
     * @param  string  $text   包含一个占位符的字符串
     * @param  mixed   $value  需要替换的值
     * @return string
     */
    public function quoteInto()
    {
        $args = func_get_args();
        foreach ($args as $key => &$value)
            $value = $key == 0 ? str_replace('?', '%s', $value) : $this->quote($value);
        return call_user_func_array('sprintf', $args);
    }


    /**
     * 分析并添加识别引用符及 AS 条件
     *
     * @param  mixed    $ident  表名/字段名
     * @param  string   $alias  别名
     * @param  boolean  $auto   是否自动替换
     * @return string
     */
    public function quoteAs($ident, $alias = null, $auto = false)
    {
        if ($ident instanceof EGP_Db_Expr) {
            $quoted = $ident->__toString();
        } else {
            is_string($ident) && $ident = explode('.', $ident);
            if (is_array($ident)) {
                foreach ($ident as $segment)
                    $segments[] = ($segment instanceof EGP_Db_Expr) ?
                                        $segment->__toString() :
                                        $this->_quoteIdent($segment, $auto);
                $alias !== null && end($ident) == $alias && $alias = null;
                $quoted = implode('.', $segments);
            } else {
                $quoted = $this->_quoteIdent($ident, $auto);
            }
        }
        $alias !== null && $quoted .= ' AS ' . $this->_quoteIdent($alias, $auto);
        return $quoted;
    }

    /**
     * 为表名补充前缀字符
     *
     * @param  string  $table  表名
     * @return string
     */
    public function quotePrefix($table)
    {
        $fix = isset($this->_options['prefix']) ? $this->_options['prefix'] : null;
        return $fix . $table;
    }

    /**
     * 分析并添加识别引用符
     *
     * @param  mixed    $ident  表名/字段名
     * @param  boolean  $auto   是否自动替换
     * @return string
     */
    public function quoteIdent($ident, $auto = false)
    {
        return $this->quoteAs($ident, null, $auto);
    }

    /**
     * 过滤转义字符
     *
     * @param  mixed   $value
     * @return string
     */
    protected function _quote($value)
    {
        return addcslashes($value, "\000\n\r\\'\"\032");
    }

    /**
     * 添加识别引用符
     *
     * @param  string   $value
     * @param  boolean  $auto
     * @return string
     */
    protected function _quoteIdent($value, $auto = false)
    {
        return $value;
    }

    /**
     * 生成查询条件
     *
     * @param  mixed  $where
     * @return string
     */
    protected function _whereExpr($where) {
        if (!empty($where)) {
            !is_array($where) && $where = array($where);
            foreach ($where as &$term)
                $term = '(' . $term . ')';
            $where = implode(' AND ', $where);
        }
        return $where;
    }

    //数据库操作的方法
    //abstract public function create();
    //abstract public function name();
    //abstract public function drop();
    //abstract public function listDbs();

    //数据表操作方法
    //abstract public function listTables();
    //abstract public function fieldTable();
    //abstract public function tablename();

    //字段操作方法
    //abstract public function fieldFlags();
    //abstract public function fieldLen();
    //abstract public function listFields();
    //abstract public function numFields();
    //abstract public function fieldName();
    //abstract public function fieldSeek();
    //abstract public function fieldType();

    //查询操作方法
    //abstract public function fetchLengths();
    //abstract public function freeResult();
    //abstract public function dataSeek();

}